"""Utilities for converting event logs into scored memory records."""
from __future__ import annotations

import datetime as dt
from dataclasses import dataclass
from math import exp, log
from typing import Iterable

from agent_society.memory.event_log import Event
from agent_society.memory.store import MemoryDocument, MemoryStore


DECAY_HALF_LIFE_HOURS = 12.0


@dataclass
class MemoryRecord:
    id: str
    owner: str
    actor: str
    content: str
    created_at: dt.datetime
    importance: float
    last_accessed: dt.datetime
    relevance: float = 0.0

    @property
    def recency_score(self) -> float:
        delta = dt.datetime.utcnow().replace(tzinfo=dt.timezone.utc) - self.last_accessed
        hours = max(delta.total_seconds() / 3600.0, 0.0)
        if hours <= 0:
            return 1.0
        half_life = max(DECAY_HALF_LIFE_HOURS, 1e-6)
        decay_constant = log(2.0) / half_life
        return exp(-decay_constant * hours)

    @property
    def score(self) -> float:
        recency = self.recency_score
        importance = max(0.0, min(self.importance, 1.0))
        relevance = max(0.0, min(self.relevance, 1.0))
        return 0.5 * importance + 0.3 * recency + 0.2 * relevance

    def as_dict(self) -> dict[str, object]:
        return {
            "id": self.id,
            "owner": self.owner,
            "actor": self.actor,
            "content": self.content,
            "importance": round(self.importance, 3),
            "recency": round(self.recency_score, 3),
            "relevance": round(self.relevance, 3),
            "score": round(self.score, 3),
            "createdAt": self.created_at,
            "lastAccessed": self.last_accessed,
        }


def _parse_timestamp(value: str) -> dt.datetime:
    normalized = value.replace("Z", "+00:00")
    try:
        parsed = dt.datetime.fromisoformat(normalized)
    except ValueError:
        parsed = dt.datetime.utcnow().replace(tzinfo=dt.timezone.utc)
    if parsed.tzinfo is None:
        parsed = parsed.replace(tzinfo=dt.timezone.utc)
    return parsed


def _importance_from_event(event: Event) -> float:
    base = 0.2
    event_type = event.type.lower()
    if any(token in event_type for token in ("reflect", "summary", "analysis")):
        base = 0.9
    elif any(token in event_type for token in ("plan", "decision", "task")):
        base = 0.7
    elif event_type in {"post", "comment", "note"}:
        base = 0.4

    content = event.content.lower()
    if "milestone" in content or "summary" in content:
        base += 0.1
    if "blocker" in content or "issue" in content:
        base += 0.15
    if "handoff" in content or "ready" in content:
        base += 0.05

    if event.headers.get("status") in {"done", "in_progress"}:
        base += 0.1
    if event.headers.get("severity") in {"high", "critical"}:
        base += 0.15

    return max(0.0, min(base, 1.0))


def events_to_records(events: Iterable[Event], *, query: str | None = None, top_k: int = 20) -> list[MemoryRecord]:
    docs: list[MemoryDocument] = []
    records: list[MemoryRecord] = []
    for event in events:
        created = _parse_timestamp(event.created_at)
        importance = _importance_from_event(event)
        record = MemoryRecord(
            id=event.id,
            owner=event.owner,
            actor=event.actor,
            content=event.content or f"Event {event.type}",
            created_at=created,
            last_accessed=created,
            importance=importance,
        )
        records.append(record)
        docs.append(MemoryDocument(content=record.content, tags={"id": record.id}))

    if query and records:
        store = MemoryStore()
        store.bulk_add(docs)
        results = store.search(query, top_k=len(records))
        relevance_map = {doc.tags.get("id"): (index + 1) for index, doc in enumerate(results)}
        if relevance_map:
            max_rank = max(relevance_map.values())
            for record in records:
                rank = relevance_map.get(record.id)
                if rank:
                    # Convert rank into [0,1] relevance score (higher rank => higher score)
                    record.relevance = 1.0 - ((rank - 1) / max(1, max_rank))

    reference = dt.datetime.utcnow().replace(tzinfo=dt.timezone.utc)
    sorted_records = sorted(records, key=lambda rec: rec.score, reverse=True)
    if top_k:
        return sorted_records[:top_k]
    return sorted_records
